/**
 *  @file  TcpAcceptor.h version 1.0 2007-11-20 $
 *  @brief Define wrapper class TemplateTcpAcceptor for acceptor socket.
 *  Copyright (c) 2007 by @author Zhao Jun <abitfree@gmail.com>
 */

#ifndef __TCP_ACCEPTOR_H__
#define __TCP_ACCEPTOR_H__

#include "TcpStream.h"
#include "InternetAddress.h"

namespace vs {
namespace net {

/**
@brief Wrapper class for accept socket.
This class is a factory, and accept new socket streams.
*/
template<typename SOCK>
class TemplateTcpAcceptor : public SOCK
{
	/// The default listen queue size.
	static const int DEFAULT_LISTEN_QUEUE_SIZE = 10;
	
public:
	///@{
	/// Default constructor.
	TemplateTcpAcceptor(int backlog = DEFAULT_LISTEN_QUEUE_SIZE);
	///@}
	
	///@{
	/// Prepare to accept socket stream from client.
	bool Listen(const InternetAddress &address);
	bool Listen(unsigned short port, const char *address = 0);
	///@}
	
	///@{
	/// Accept socket stream from client.
	SOCKET_HANDLE Accept(InternetAddress &address);
	bool Accept(InternetAddress &address, TemplateTcpStream<SocketHandle> &socket_stream);
	bool Accept(InternetAddress &address, TemplateTcpStream<AsynSocketHandle> &socket_stream);
	///@}

	/// Get the listen queue size.
	int GetListenQueueLength();
	
	/// Set the listen queue size.
	void SetListenQueueLength(int length);

private:
	// max client number in the listen queue.
	int listen_queue_length_;
};


/// The blocking TCP acceptor class.
typedef TemplateTcpAcceptor<SocketHandle> TcpAcceptor;

/// The non-blocking TCP acceptor class.
typedef TemplateTcpAcceptor<AsynSocketHandle> AsynTcpAcceptor;

template<typename SOCK>
INLINE
TemplateTcpAcceptor<SOCK>::TemplateTcpAcceptor(int backlog)
	: listen_queue_length_(backlog)
{
}

template<typename SOCK>
INLINE
bool TemplateTcpAcceptor<SOCK>::Listen(const InternetAddress &address)
{
	if (!IsValid())
	{
		if (!Create(AF_INET, SOCK_STREAM, 0))
		{
			net_debug("TemplateTcpAcceptor::Listen() creates socket failed, error:%d\n", GetNetError());
			return false;
		}
	}
	
	const int one = 1;
	if (setsockopt(GetHandle(), SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one)) == SOCKET_ERROR)
	{
		net_debug("TemplateTcpAcceptor::Listen() setsockopt falied, error:%d\n", GetNetError());
		Close();
		return false;
	}
	
	if (bind(GetHandle(), (struct sockaddr*)address.GetAddress(), address.GetAddressSize()) == SOCKET_ERROR)
	{
		net_debug("TemplateTcpAcceptor::Listen() bind falied, error:%d\n", GetNetError());
		Close();
		return false;
	}	
 
	if (listen(GetHandle(), listen_queue_length_) == SOCKET_ERROR)
	{
		net_debug("TemplateTcpAcceptor::Listen() listen falied, error:%d\n", GetNetError());
		Close();
		return false;
	}

	return true;
}

template<typename SOCK>
INLINE
bool TemplateTcpAcceptor<SOCK>::Listen(unsigned short port, const char *address)
{
	InternetAddress internet_address(port, address);
	return Listen(internet_address);
}

template<typename SOCK>
INLINE
SOCKET_HANDLE TemplateTcpAcceptor<SOCK>::Accept(InternetAddress &address)
{
	SOCKET_HANDLE client_socket = INVALID_SOCKET_HANDLE;
	struct sockaddr *socket_addr = (struct sockaddr*)address.GetAddress();
	SOCKETLEN_T length = address.GetAddressSize();
	while ((client_socket = accept(GetHandle(), socket_addr, &length)) == INVALID_SOCKET_HANDLE)
	{
#ifdef WIN32
		int error = WSAGetLastError();
		if (error == WSAEWOULDBLOCK)  // non-blocking
		{
			net_debug("TemplateTcpAcceptor::Accept() WSAEWOULDBLOCK\n");
			break;
		}
#else
		if (errno == EINTR)  // interrupted by signal
		{
			net_debug("TemplateTcpAcceptor::Accept() EINTR\n");
			continue;
		}
		else if (errno == EWOULDBLOCK)  // non-blocking
		{
			net_debug("TemplateTcpAcceptor::Accept() EWOULDBLOCK\n");
			break;
		}
#endif
		net_debug("TemplateTcpAcceptor::Accept() accept falied, error:%d\n", GetNetError());
	}

	return client_socket;
}

template<typename SOCK>
INLINE
bool TemplateTcpAcceptor<SOCK>::Accept(InternetAddress &address, TemplateTcpStream<SocketHandle> &socket_stream)
{
	SOCKET_HANDLE client_socket = Accept(address);
	if (client_socket == INVALID_SOCKET_HANDLE)
	{
		return false;
	}
	else
	{
		socket_stream.Attach(client_socket);
		return true;
	}
}

template<typename SOCK>
INLINE
bool TemplateTcpAcceptor<SOCK>::Accept(InternetAddress &address, TemplateTcpStream<AsynSocketHandle> &socket_stream)
{
	SOCKET_HANDLE client_socket = Accept(address);
	if (client_socket == INVALID_SOCKET_HANDLE)
	{
		return false;
	}
	else
	{
		AsynSocketHandle::ToNonblocking(client_socket);
		socket_stream.Attach(client_socket);
		return true;
	}
}

}	// namespace net
}	// namespace vs


#endif	// __TCP_ACCEPTOR_H__
